package com.heima.lock.controller;

import com.heima.lock.mapper.MyLockMapper;
import com.heima.lock.pojos.MyLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class StockController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MyLockMapper myLockMapper;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 不使用分布式锁存在问题：多线程并发访问 分布式环境 数据不安全
     *
     * @return
     */
    @GetMapping("/stock")
    public String stock() {
        synchronized ("lock") {
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }
        }
        return "OK";
    }

    /**
     * 分布式锁实现 -- 基于mysql unique索引实现
     *
     * @return
     */
    @GetMapping("/stock1")
    public String stock1() {
        //往数据库中存入一条数据： 'lock'  , 当前线程id
        try {
            //1. 抢分布式锁
            MyLock myLock = new MyLock("lock", String.valueOf(Thread.currentThread().getId()));
            myLockMapper.insert(myLock);
            //2. 执行业务操作
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }
            //3. 释放锁
            myLockMapper.deleteById(myLock.getId());
        } catch (Exception e) {
            System.out.println("手慢了，秒杀没抢到!");
        }
        return "OK";
    }

    /**
     * 分布式锁实现 -- 基于redis setNX命令实现
     *
     * @return
     */
    @GetMapping("/stock2")
    public String stock2() {
        try {
            //往redis中存入一条数据： 'lock'  , 当前线程id
            //1. 抢分布式锁
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent("lock", String.valueOf(Thread.currentThread().getId()));
            if (isLock) {
                //2. 执行业务操作
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                } else {
                    System.out.println("库存不足！！！");
                }
            } else {
                System.out.println("手慢了，秒杀没抢到!");
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            //3. 释放锁
            String threadId = redisTemplate.opsForValue().get("lock");
            if (String.valueOf(Thread.currentThread().getId()).equals(threadId)) {
                redisTemplate.delete("lock");
            }
        }

        return "OK";
    }

    /**
     * 分布式锁实现 -- 基于redis setNX命令实现
     * 自己实现存在的问题：
     *      1. 当一个线程占用锁后，线程进入阻塞，锁不会自动过期
     *      2. 锁一旦设置了过期时间，怎么来给线程进行自动续期？    -- redission   watch dog
     *
     * @return
     */
    @GetMapping("/stock3")
    public String stock3(@RequestParam String name) {
        try {
            //往redis中存入一条数据： 'lock'  , 当前线程id
            //1. 抢分布式锁
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent("lock", String.valueOf(Thread.currentThread().getId()),5, TimeUnit.SECONDS);
            if (isLock) {
                //2. 执行业务操作
                if("abc".equals(name)) {
                    Thread.sleep(8000);
                }
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                } else {
                    System.out.println("库存不足！！！");
                }
            } else {
                System.out.println("手慢了，秒杀没抢到!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //3. 释放锁
            String threadId = redisTemplate.opsForValue().get("lock");
            if (String.valueOf(Thread.currentThread().getId()).equals(threadId)) {
                redisTemplate.delete("lock");
            }
        }

        return "OK";
    }


    /**
     * 分布式锁实现 -- 基于redisson框架实现
     * 自己实现存在的问题：
     *      1. 当一个线程占用锁后，线程进入阻塞，锁不会自动过期
     *      2. 锁一旦设置了过期时间，怎么来给线程进行自动续期？    -- redission   watch dog
     * @return
     */
    @GetMapping("/stock4")
    public String stock4(@RequestParam String name) {
        //注意：这里仅仅是创建了一个java对象，并没有操作redis
        RLock lock = redissonClient.getLock("lock");
        try {
            //往redis中存入一条数据： 'lock'  , 当前线程id
            //1. 抢分布式锁
            boolean isLock = lock.tryLock();
            if (isLock) {
                //2. 执行业务操作
                if("abc".equals(name)) {
                    Thread.sleep(40000);
                }
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                } else {
                    System.out.println("库存不足！！！");
                }
            } else {
                System.out.println("手慢了，秒杀没抢到!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //3. 释放锁
            lock.unlock();
        }
        return "OK";
    }


    public static void main(String[] args) {
        method();
    }

    private static void method() {
        method();
    }
}